package com.cskaoyan.wordmemorize.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cskaoyan.wordmemorize.common.exception.ExcelParseException;
import com.cskaoyan.wordmemorize.dao.entity.VocBookDO;
import com.cskaoyan.wordmemorize.dao.entity.VocChapterDO;
import com.cskaoyan.wordmemorize.dao.mapper.VocBookMapper;
import com.cskaoyan.wordmemorize.dao.mapper.VocChapterMapper;
import com.cskaoyan.wordmemorize.dto.admin.VocExcelDTO;
import lombok.extern.slf4j.Slf4j;

import java.util.Objects;

import static com.cskaoyan.wordmemorize.common.constant.ResultCodeEnum.VOC_EXCEL_DATA_ERROR;

/**
 * VocExcelListener 是 EasyExcel 的读取监听器，用于逐行处理 Excel 文件中的数据。
 * 它会在读取每一行数据时被触发，并执行校验、存储等操作。
 */

// 有个很重要的点 DemoDataListener 不能被spring管理，要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
//@Component
public class ValidateVocExcelListener implements ReadListener<VocExcelDTO> {

//    private DemoMapper demoMapper;


//    /**
//     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
//     *
//     * @param demoDAO
//     */
//    public VocExcelListener(DemoMapper demoMapper) {
//        this.demoMapper = demoMapper;
//    }

    private final VocBookMapper vocBookMapper;
    private final VocChapterMapper vocChapterMapper;

//    @Autowired
    public ValidateVocExcelListener(VocBookMapper vocBookMapper, VocChapterMapper vocChapterMapper) {
        this.vocBookMapper = vocBookMapper;
        this.vocChapterMapper = vocChapterMapper;
    }

    /**
     * 每当 EasyExcel 读取一行数据时，就会调用这个方法。
     * 这里进行数据校验和数据存储。
     *
     * @param vocExcelDTO 当前行映射成的 Java 对象
     * @param analysisContext 上下文信息，如当前行号
     */
    @Override
    public void invoke(VocExcelDTO vocExcelDTO, AnalysisContext analysisContext) {
        // 打印当前行号和读取到的数据
        int rowIndex = analysisContext.readRowHolder().getRowIndex();
        log.info("解析到第 {} 行数据: {}", rowIndex + 1, JSON.toJSONString(vocExcelDTO));

        // 校验字段是否为空
        validateEmptyFields(vocExcelDTO, rowIndex);

        // 校验释义格式是否为：词性. 释义内容（多个用换行分隔）
        validateInterpretations(vocExcelDTO, rowIndex);

        // 校验例句格式是否为：英文例句\n中文翻译（交替出现）
        validateExampleSentences(vocExcelDTO, rowIndex);

        // 校验书籍信息是否存在
        validateBookInfo(vocExcelDTO, rowIndex);

        // 保存数据（模拟）
//        saveData(vocExcelDTO);
    }

    /**
     * 所有数据读取完成后调用的方法
     * @param analysisContext 上下文信息
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        log.info("所有数据解析完成！");
    }

    /**
     * 控制是否继续读取下一行
     * 默认返回 true，表示继续读取
     */
    @Override
    public boolean hasNext(AnalysisContext context) {
        return ReadListener.super.hasNext(context);
    }

    /**
     * 校验字段是否为空
     * @param vocExcelDTO 当前行数据
     * @param rowIndex 当前行号（从0开始）
     */
    private void validateEmptyFields(VocExcelDTO vocExcelDTO, int rowIndex) {
        // 校验单词是否为空
        if (Objects.isNull(vocExcelDTO.getContent()) || vocExcelDTO.getContent().trim().isEmpty()) {
            throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"单词为空");
        }

        // 校验英式音标是否为空
        if (Objects.isNull(vocExcelDTO.getUkPhonetic()) || vocExcelDTO.getUkPhonetic().trim().isEmpty()) {
            throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"英式音标为空");
        }

        // 校验美式音标是否为空
        if (Objects.isNull(vocExcelDTO.getUsPhonetic()) || vocExcelDTO.getUsPhonetic().trim().isEmpty()) {
            throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"美式音标为空");
        }

        // 校验英式发音文件名是否为空
        if (Objects.isNull(vocExcelDTO.getUkPronounciation()) || vocExcelDTO.getUkPronounciation().trim().isEmpty()) {
            throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"英式发音文件名为空");
        }

        // 校验美式发音文件名是否为空
        if (Objects.isNull(vocExcelDTO.getUsPronounciation()) || vocExcelDTO.getUsPronounciation().trim().isEmpty()) {
            throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"美式发音文件名为空");
        }

        // 校验释义是否为空
        if (Objects.isNull(vocExcelDTO.getInterpretations()) || vocExcelDTO.getInterpretations().trim().isEmpty()) {
            throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"释义为空");
        }

        // 校验例句是否为空
        if (Objects.isNull(vocExcelDTO.getExampleSentences()) || vocExcelDTO.getExampleSentences().trim().isEmpty()) {
            throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"例句为空");
        }

        // 校验书籍信息是否为空
        if (Objects.isNull(vocExcelDTO.getBookInfos()) || vocExcelDTO.getBookInfos().trim().isEmpty()) {
            throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"书籍信息为空");
        }
    }

    /**
     * 校验释义格式是否为：词性. 释义内容（多个用换行分隔）
     * 示例：n. 名词释义\nv. 动词释义
     * @param vocExcelDTO 当前行映射成的 Java 对象
     * @param rowIndex 当前行索引（从0开始）
     */
    private void validateInterpretations(VocExcelDTO vocExcelDTO, int rowIndex) {
        // 将释义字段按换行符分割成多个释义条目
        String[] lines = vocExcelDTO.getInterpretations().split("\n");

        // 遍历每一条释义
        for (int i = 0; i < lines.length; i++) {
            String line = lines[i].trim();

            // 使用正则表达式校验每条释义是否符合格式：词性. 释义内容
            // 正则解释：^[a-zA-Z]+\.\\s*.+
            // - ^ 表示开头
            // - [a-zA-Z]+ 表示一个或多个英文字母（词性）
            // - \. 表示一个点号
            // - \s* 表示零个或多个空格
            // - .+ 表示一个或多个任意字符（释义内容）
            if (!line.matches("^[a-zA-Z]+\\.\\s*.+")) {
                // 如果不符合格式，抛出异常并指出具体哪一行哪一条释义出错
                throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"释义格式错误,应为词性.释义内容");
            }
        }
    }

    /**
     * 校验例句格式是否为：英文例句\n中文翻译\n英文例句\n中文翻译...（交替出现）
     * 示例：He runs fast.\n他跑得很快。
     * @param vocExcelDTO 当前行数据
     * @param rowIndex 当前行号
     */
    private void validateExampleSentences(VocExcelDTO vocExcelDTO, int rowIndex) {
        // 将例句字段按换行符分割成数组
        String[] lines = vocExcelDTO.getExampleSentences().split("\n");

        // 校验例句与翻译是否成对出现（即总行数必须是偶数）
        if (lines.length % 2 != 0) {
            throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"例句格式错误,应为英文例句\n中文翻译");
        }

        // 遍历每一对（英文 + 中文）例句和翻译（每次跳2步）
        for (int i = 0; i < lines.length; i += 2) {
            String enLine = lines[i].trim();
            String zhLine = lines[i + 1].trim();

            // 校验是否为空
            if (enLine.isEmpty() || zhLine.isEmpty()) {
                throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"例句中英文或翻译为空");
            }

            // 校验是否是英文 + 中文 的结构
            if (!isEnglish(enLine)) {
                throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"例句格式错误,第 " + (i + 1) + " 条应为英文例句");
            }
            if (!isChinese(zhLine)) {
                throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"例句格式错误,第 " + (i + 2) + " 条应为中文翻译");
            }
        }
    }

    /**
     * 判断字符串是否为英文（允许空格、标点符号）
     *
     * 正则表达式解释：
     * ^                       表示字符串的开头
     * [a-zA-Z0-9              匹配英文字母（大小写）和数字
     * \\s                     匹配任意空白字符（如空格、制表符等）
     * \\p{Punct}              匹配任何标点符号（如 . , ! ? : ; 等）
     * ]*                      表示前面的字符可以出现 0 次或多次
     * $                       表示字符串的结尾
     *
     * 含义：整个字符串只能包含英文字符、数字、空格和标点符号。
     */
    private boolean isEnglish(String text) {
        // 匹配英文字符、数字、标点符号和空格
        return text.matches("^[a-zA-Z0-9\\s\\p{Punct}]*$");
    }


    /**
     * 判断字符串是否为中文（包含中文字符）
     *
     * 正则表达式解释：
     * .*                      表示任意字符（0 个或多个）
     * [\\u4e00-\\u9fa5]       匹配一个中文字符（Unicode 范围：4e00 - 9fa5）
     * +                       表示前面的中文字符至少出现一次
     * .*                      表示任意字符（0 个或多个）
     *
     * 含义：只要字符串中包含至少一个中文字符，就返回 true。
     */
    private boolean isChinese(String text) {
        return text.matches(".*[\\u4e00-\\u9fa5]+.*");
    }

    /**
     * 校验书籍信息是否存在（调用mapper方法）
     * 格式应为：书籍名称#章节名称
     * @param vocExcelDTO 当前行数据
     * @param rowIndex 当前行号
     */
    private void validateBookInfo(VocExcelDTO vocExcelDTO, int rowIndex) {
        String[] bookInfo = vocExcelDTO.getBookInfos().split("#");
        if (bookInfo.length != 2 || bookInfo[0].trim().isEmpty() || bookInfo[1].trim().isEmpty()) {
            throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"书籍信息格式错误,应为：书籍名称#章节名称");
        }

        // 调用mapper方法判断书籍和章节是否存在
        // 判断书籍是否存在
        LambdaQueryWrapper<VocBookDO> vocBookDOQueryWrapper = new LambdaQueryWrapper<>();
        vocBookDOQueryWrapper.eq(VocBookDO::getName, bookInfo[0]);
        VocBookDO vocBookDO = vocBookMapper.selectOne(vocBookDOQueryWrapper);

        if (Objects.isNull(vocBookDO)) {
            throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"书籍不存在：" + bookInfo[0]);
        }

        // 判断章节是否存在
        LambdaQueryWrapper<VocChapterDO> vocChapterDOQueryWrapper = new LambdaQueryWrapper<>();
        vocChapterDOQueryWrapper.eq(VocChapterDO::getChapterName, bookInfo[1]);
        VocChapterDO vocChapterDO = vocChapterMapper.selectOne(vocChapterDOQueryWrapper);

        if (Objects.isNull(vocChapterDO)) {
            throw new ExcelParseException(VOC_EXCEL_DATA_ERROR,rowIndex + 1,"章节不存在：" + bookInfo[1]);
        }
    }

    /**
     * 存储数据（模拟）
     * 实际开发中应调用 Mapper 或 Service 将数据保存到数据库
     * @param vocExcelDTO 当前行数据
     */
    private void saveData(VocExcelDTO vocExcelDTO) {
        log.info("开始存储数据库：{}", vocExcelDTO.getContent());
        // 示例：vocMapper.insert(vocExcelDTO);
        log.info("存储数据库成功");
    }
}